﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using Microsoft.WindowsAzure;
using FtpWorkerRole.FtpCommand;
using FtpWorkerRole.AzureStorage;
using System.Reflection;
using Microsoft.WindowsAzure.ServiceRuntime;

namespace FtpWorkerRole.FtpServer
{
    public class FtpSocketHandler
    {
        private TcpClient client;
        private Dictionary<String, CommandBase> commands;
        private bool loginFlag = false;
        public string dataChannelSocketAddress ="";
        public string currentDirectory="\\";
        public int dataChannelSocketPort { get; set; }
        public string user { get; set; }
        public IFileSystem fileSystem { get; set; }

        public bool Login
        {
            get { return loginFlag; }
        }

        public FtpSocketHandler()
        {
            addCommands();            
        }

        private void addCommands()
        {
            commands = new Dictionary<string, CommandBase>();
            addCommand(new QuitCommand(this));
            addCommand(new UserCommand(this));
            addCommand(new PassCommand(this));
            addCommand(new StorCommand(this));
            addCommand(new RetrCommand(this));
            addCommand(new DeleCommand(this));
            addCommand(new ListCommand(this));
            addCommand(new PortCommand(this));

            addCommand(new PwdCommand(this));
            addCommand(new CwdCommand(this));
            addCommand(new CdupCommand(this));
            addCommand(new MkdCommand(this));
            addCommand(new RmdCommand(this));
            addCommand(new TypeCommand(this));
        }

        private void addCommand(CommandBase command)
        {
            commands[command.commandName] = command;
        }

        public bool login(string userName, string password)
        {
            loginFlag = AzureUser.login(userName, password);
            if (loginFlag)
            {
                var containername = AzureUser.isShared(userName);
                fileSystem = AzureFactory.createFileSystem(containername);
            }
            return loginFlag;
        }

        public void start(TcpClient client)
        {
            this.client = client;
            Thread thread = new Thread(new ThreadStart(round));
            thread.Start();
        }

        private void round()
        {            
            NetworkStream networkStream = client.GetStream();
            String ipStr = GetRemoteIP(client);
            String hello = String.Format("{0} {1}\r\n", 220, "Hello " + ipStr);
            Utils.sendMessage(client, hello);

            var ctx = new ConnectionContext(CloudStorageAccount.Parse(
                  RoleEnvironment.GetConfigurationSettingValue(
                  "Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString")));
            var conn = new ConnectionEntity(ipStr);
            conn.IP = ipStr;
            conn.port = GetRemotePort(client);
            ctx.AddConnection(conn);

            try
            {
                byte[] receiveMessage = new byte[256];
                String echo = "";
                networkStream = client.GetStream();
                int received = networkStream.Read(receiveMessage, 0, receiveMessage.Length);
                while (received > 0)
                {
                    String receiveString = Encoding.ASCII.GetString(receiveMessage, 0, received);
                    echo += receiveString;
                    if (echo.Contains('\n'))
                    {
                        string messages = echo.Substring(0, echo.IndexOf('\r'));
                        string commandStr;
                        string parameter;
                        int spaceIndex = echo.IndexOf(' ');
                        if (spaceIndex < 0)
                        {
                            commandStr = messages.ToUpper();
                            parameter = "";
                        }
                        else
                        {
                            commandStr = messages.Substring(0, spaceIndex).ToUpper();
                            parameter = messages.Substring(commandStr.Length + 1);
                        }
                        if (commands.ContainsKey(commandStr))
                        {
                            var command = commands[commandStr];
                            command.execute(parameter);
                            if (commandStr == "QUIT")
                                break;
                        }
                        else
                        {
                            //Utils.sendMessage(client, echo);
                            Utils.sendMessage(client, "550 Unkown Command " + echo + "\r\n");
                        }
                        echo = "";
                    }
                    received = networkStream.Read(receiveMessage, 0, receiveMessage.Length);
                }
                client.Close();
            }
            catch (Exception)
            {
            }
            finally
            {
                ctx.DeleteConnection(conn);
            }
        }
        public TcpClient getSocket()
        {
            return client;
        }

        string GetRemoteIP(TcpClient cln)
        {
            string ip = cln.Client.RemoteEndPoint.ToString().Split(':')[0];
            return ip;
        }

        int GetRemotePort(TcpClient cln)
        {
            string temp = cln.Client.RemoteEndPoint.ToString().Split(':')[1];
            int port = Convert.ToInt32(temp);
            return port;
        } 
    }
}
